
#include <jni.h>
#include <android/log.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <pthread.h>

#include "uac_jni.h"
#include "uac.h"

//#include <map>
//#include <stdlib.h>

#define  COSDKLOG(arg) LOGI("%s:%d: %s ", __FILE__, __LINE__, "OnLoad called success");

#define JAVA_CLASS_JAVA2C_NAME "com/goke/media/jni/Java2CAPI"
#define JAVA_CLASS_C2JAVA_NAME "com/goke/media/jni/C2JavaAPI"

#define JOWW(rettype, name)                                             \
  rettype JNIEXPORT JNICALL Java_com_goke_media_jni_Java2CAPI_##name

#define SAFERLEASE(p) free(p),p=NULL

#define PENV (*env)
#define POS()  LOGI("%s %d", __FUNCTION__, __LINE__)

int SdkCallback(void *void_env, int s, int c, char *pMsg);

static JavaVM* g_vm = 0;
jclass g_CosdkCallBackClass = 0;
jobject g_CosdkCallBackObject = 0;
jmethodID g_CosdkCallBackMethod = 0;

/*此函数需要放在SDK消息线程内调用*/
void* AttachCurrentThread2JavaVM()
{
    JNIEnv *env = NULL;
    int status = (*g_vm)->AttachCurrentThread(g_vm, &env, NULL);
    return (void*)env;
}

/*此函数需要放在SDK消息线程内调用*/
void  DetachCurrentThreadFromJavaVM()
{
    (*g_vm)->DetachCurrentThread(g_vm);
}

jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
  // Only called once.
  //CHECK(!g_vm, "OnLoad called more than once");
  if(g_vm)
  {
    LOGE("%s:%d: %s ", __FILE__, __LINE__, "OnLoad called more than once");
  }
  else{
    LOGI("%s:%d: %s ", __FILE__, __LINE__, "OnLoad called success");
  }

  g_vm = vm;
  return JNI_VERSION_1_4;
}

#if 1
int InitSDKJni(JNIEnv *env)
{
    //static bool bInited = false;
    //if(bInited)
    //{
    //return true;    /*add entry protection */
    //}

    jclass tmp_class = PENV->FindClass(env, JAVA_CLASS_C2JAVA_NAME);
    g_CosdkCallBackClass = PENV->NewGlobalRef(env, tmp_class);
    PENV->DeleteLocalRef(env, tmp_class);
    if(g_CosdkCallBackClass == NULL)
    {
        return -1;
    }

    /*jmethodID construction_id = PENV->GetMethodID(env, g_CosdkCallBackClass, "<init>", "()V");
    if (construction_id == 0)
    {
        return -1;
    }
    jobject temp_object = PENV->NewObject(env, g_CosdkCallBackClass, construction_id);*/
    jobject temp_object = PENV->AllocObject(env, g_CosdkCallBackClass);
    g_CosdkCallBackObject = PENV->NewGlobalRef(env, temp_object);
    PENV->DeleteLocalRef(env, temp_object);
    if (g_CosdkCallBackObject == NULL)
    {
        return -1;
    }

    g_CosdkCallBackMethod = PENV->GetMethodID(env, g_CosdkCallBackClass, "SdkCallback", "(IILjava/lang/String;Ljava/lang/String;)I");
    if (g_CosdkCallBackMethod == NULL)
    {
        PENV->DeleteGlobalRef(env, g_CosdkCallBackObject);
        PENV->DeleteGlobalRef(env, g_CosdkCallBackClass);
        return -1;
    }

    return 0;
}

void ExitSDKJni(JNIEnv *env){
  PENV->DeleteGlobalRef(env, g_CosdkCallBackObject);
  g_CosdkCallBackObject = NULL;
  PENV->DeleteGlobalRef(env,g_CosdkCallBackClass);
  g_CosdkCallBackClass = NULL;
}
#endif

/*char *jstringTostring(JNIEnv *env, jstring jstr)
{
  char *rtn = NULL;
  jclass clsstring = PENV->FindClass(env, "java/lang/String");
  jstring strencode = PENV->NewStringUTF(env,"utf-8");
  jmethodID mid = PENV->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
  jbyteArray barr = (jbyteArray)PENV->CallObjectMethod(env,jstr, mid, strencode);
  jsize alen = PENV->GetArrayLength(env,barr);
  jbyte *ba = PENV->GetByteArrayElements(env, barr, JNI_FALSE);
  if (alen > 0)
  {
    rtn = (char *)malloc(alen + 1);
    memcpy(rtn, ba, alen);
    rtn[alen] = 0;
  }
  PENV->ReleaseByteArrayElements(env, barr, ba, 0);
  //PENV->DeleteLocalRef(env, clsstring);   // zth add
  return rtn;
}

jstring stringTojstring(JNIEnv *env, const char *pat)
{
  jclass strClass = PENV->FindClass(env, "java/lang/String");

  jmethodID ctorID = PENV->GetMethodID(env,strClass, "<init>", "([BLjava/lang/String;)V");

  jstring encoding = PENV->NewStringUTF(env,"utf-8");

  jbyteArray bytes = PENV->NewByteArray(env,strlen(pat));

  PENV->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte *)pat);

  jstring str = (jstring)PENV->NewObject(env,strClass, ctorID, bytes, encoding);
  //PENV->DeleteLocalRef(env, strClass);   // zth add

  return str;
}*/

 char* jstringTostring(JNIEnv *jenv, jstring jarg1) {
    char *arg1 = NULL;
    char *dststring = NULL;
    if (jarg1) {
        arg1 = (char *)(*jenv)->GetStringUTFChars(jenv, jarg1, 0);
        if (!arg1) return NULL;
    }
    {

        if (arg1) {
            dststring = (char *) malloc(strlen((const char *)arg1)+1);
            strcpy((char *)dststring, (const char *)arg1);
        } else {
            dststring = NULL;
        }
    }
    if (arg1) (*jenv)->ReleaseStringUTFChars(jenv, jarg1, (const char *)arg1);
    return dststring;
}


 jstring stringTojstring(JNIEnv *jenv, const char * srcstring) {
jstring jresult = 0 ;
char *result = (char *)srcstring;

if (result) jresult = (*jenv)->NewStringUTF(jenv, (const char *)result);
return jresult;
}

// 回调
int SdkCallback(void *void_env, int s, int c, char *pMsg)
{
  JNIEnv *env = (JNIEnv*)void_env;
  int i;
  jstring tmp = NULL;
  if(pMsg != NULL)
  {
    tmp = stringTojstring(env, pMsg);
  }

  jint ret=-1;
  for(i=0;i<3;i++)
  {
    ret = PENV->CallIntMethod(env, g_CosdkCallBackObject, g_CosdkCallBackMethod, s, c,tmp);
    if(ret < 0)
    {
      LOGE("message callback ret=%d time %d", ret, i+1);
    }
    else
      break;
  }

  return 0;
}

JOWW(jint,cosdkInit)(JNIEnv* env, jobject jc, jobject context)
{
  InitSDKJni(env);

  return 0;
}

JOWW(jint,cosdkExit)(JNIEnv* env,jobject jc)
{
  
  ExitSDKJni(env);
  return 0;
}

JOWW(jstring,cosdkVersion)(JNIEnv* env, jobject jc)
{
    return stringTojstring(env, "20220627");
}

JOWW(jint,cosdkReg)(JNIEnv* env,jobject jc, jstring server, jstring proxy, jstring username, jstring password)
{
  LOGI("cosdkReg");

  char *pserver = jstringTostring(env, server);
  char *pproxy = jstringTostring(env, proxy);
  char *pusername = jstringTostring(env, username);
  char *ppassword = jstringTostring(env, password);

  POS();

  sip_init();

   POS();
   LOGI("here %s %s %s %s", pserver, pproxy, pusername, ppassword);
  sip_register(pserver, pproxy, pusername, ppassword);  // remote port
   POS();

  SAFERLEASE(pserver);
  SAFERLEASE(pproxy);
  SAFERLEASE(pusername);
  SAFERLEASE(ppassword);
   POS();
  return 0;
}

JOWW(jint, cosdkUnreg)(JNIEnv* env,jobject jc)
{
  LOGI("cosdkUnreg");
  sip_unregister();
  sip_exit();
  return 0;
}

JOWW(jint, cosdkInvite)(JNIEnv* env,jobject jc, jstring callee)
{
  int ret = -1;
  LOGI("cosdkInvite");
  char* pcallee = jstringTostring(env, callee);

  ret = sip_invite(pcallee);

  SAFERLEASE(pcallee);
  return (jint)ret;
}

JOWW(jint, cosdkAnswer)(JNIEnv* env,jobject jc, jint callid)
{
    int ret = -1;
    LOGI("cosdkAnswer");
    ret = sip_answer(callid);
    return (jint)ret;
}

JOWW(jint, cosdkHangup)(JNIEnv* env,jobject jc, jint callid)
{
    int ret = -1;
    LOGI("cosdkHangup");
    ret = sip_hangup(callid);
    return (jint)ret;
}

JOWW(jint, mediaCreate)(JNIEnv* env,jobject jc, jobject context, jint hasVideo)
{
    int ret = -1;
    return (jint)ret;
}

JOWW(jint, mediaDelete)(JNIEnv* env,jobject jc)
{
    int ret = -1;
    return (jint)ret;
}

JOWW(jint, mediaStartRecv)(JNIEnv* env,jobject jc, jint localport)
{
    int ret = -1;
    return (jint)ret;
}

JOWW(jint, mediaStartSend)(JNIEnv* env,jobject jc, int remoteport, jstring remoteaddr, jstring codec)
{
    int ret = -1;
    return (jint)ret;
}

JOWW(jint, mediaStop)(JNIEnv* env,jobject jc) // stop both send and recv
{
    int ret = -1;
    return (jint)ret;
}
